//from https://atomgit.com/flow-step/SetupNode

import * as step from '@flow-step/step-toolkit'
import path from 'path'
import fs from "fs"
import process from 'process'


export async function installNode(version: string, npmType: string, installPath: string | undefined): Promise<void> {
    const toolName: string = 'node'
    step.info(`Going to install node with version ${version}`)
    let latestNodeVersion: string = ""
    let nodeCachedPath: string = ""
    try {
        latestNodeVersion = await step.tool.formatToLatestPatchVersion(toolName, "v", version)
        step.info(`The latest Version is ${latestNodeVersion}`)
        let platform: string = step.platform.getPlatform()
        let toolSuffix = getToolSuffix(platform)
        nodeCachedPath = await step.tool.install(toolName, latestNodeVersion, toolSuffix)
        setupNode(nodeCachedPath)
    } catch (err) {
        throw new Error(`Install Nodejs ${version} failed, ${err}`)
    }
    const useNpmCache: string | undefined = process.env['USE_NPM_CACHE']
    if (useNpmCache == 'true') {
        await step.exec.call('npm', ['config', 'set', 'prefer-offline', 'true'])
        await step.exec.call('npm', ['config', 'set', 'audit', 'false'])
    }
    await symlinkInstallPathToCachePath(installPath, nodeCachedPath);
    await installNpm(npmType, version, nodeCachedPath)
    step.info(`Install node ${version} --> ${latestNodeVersion} successfully!`)
}

export async function symlinkInstallPathToCachePath(installPath: string | undefined, cachedPath: string) {
    if (installPath !== undefined) {
        fs.rmSync(installPath, {recursive: true, force: true})

        let msg = `Create link dir ${installPath} to ${cachedPath}`;
        try {
            fs.symlinkSync(cachedPath, installPath, 'dir')
            step.info(`${msg}`)
        } catch (err) {
            throw new Error(`${msg} failed, ${err}`)
        }
    }
}

export function setupNode(nodePath: string): void {
    if (step.platform.getPlatform() != step.platform.PLATFORM_WIN) {
        nodePath = path.join(nodePath, 'bin')
    }
    step.addPath(nodePath)
}

export function getToolSuffix(platform: string): string {
    return platform === step.platform.PLATFORM_WIN ? "zip" : "tar.gz";
}

export async function installNpm(npmType: string, version: string, nodePath: string): Promise<void> {
    const npmCommand = step.platform.getPlatform() === step.platform.PLATFORM_WIN ? 'npm.cmd' : 'npm'
    if (step.platform.getPlatform() === step.platform.PLATFORM_WIN) {
        if (!fs.existsSync(path.join(nodePath, 'node_global'))) {
            fs.mkdirSync(path.join(nodePath, 'node_global'))
        }
        if (!fs.existsSync(path.join(nodePath, 'node_cache'))) {
            fs.mkdirSync(path.join(nodePath, 'node_cache'))
        }
        await executeNpmCommand(npmCommand, ['config', 'set', 'prefix', path.join(nodePath, 'node_global')])
        await executeNpmCommand(npmCommand, ['config', 'set', 'cache', path.join(nodePath, 'node_cache')])
        step.addPath(path.join(nodePath, 'node_global'))
    }
    if (npmType === 'cnpm') {
        const cnpmVersion = '7.1.0'
        const cnpmCommand = step.platform.getPlatform() === step.platform.PLATFORM_WIN ? 'cnpm.cmd' : 'cnpm'
        if (await isCnpmOrPnpmInstalled(cnpmCommand, cnpmVersion)) {
            return
        }
        step.info(`Going to install ${npmType}`)
        if (step.platform.getPlatform() === step.platform.PLATFORM_LINUX) {
            //linux下通过解压源码包安装cnpm,避免直接通过npm命令安装cnpm耗时太长
            const cnpmCachedPath = await step.tool.install('cnpm', cnpmVersion, 'tar.gz')
            const cnpmBinPath = path.join(cnpmCachedPath, 'bin')
            step.addPath(cnpmBinPath)
        } else {
            const args = ['install', '-g', `cnpm@${cnpmVersion}`, '--registry=https://registry.npmmirror.com']
            await executeNpmCommand(npmCommand, args)
        }
    } else if (npmType === 'pnpm') {
        const pnpmCommand = step.platform.getPlatform() === step.platform.PLATFORM_WIN ? 'pnpm.cmd' : 'pnpm'
        let pnpmVersion = '7'
        const mainVersionNumber = parseInt(version.split('.')[0], 10)
        if (mainVersionNumber <= 9) {
            pnpmVersion = '2'
        } else if (mainVersionNumber <= 14) {
            pnpmVersion = '5'
        }
        if (await isCnpmOrPnpmInstalled(pnpmCommand, pnpmVersion)) {
            return
        }
        step.info(`Going to install ${npmType}@${pnpmVersion}`)
        const args = ['install', '-g', `pnpm@${pnpmVersion}`, '--registry=https://registry.npmmirror.com']
        await executeNpmCommand(npmCommand, args)
    }
}
export async function executeNpmCommand(command: string, commandArgs: string[]): Promise<number> {
    return step.exec.call(command, commandArgs, {
        listener: {
            stdoutLine: step.info
        }
    })
}
export async function isCnpmOrPnpmInstalled(command: string, version: string): Promise<boolean> {
    try {
        const output = await step.exec.callOutput(command, ['-v'])
        const versionContent = output.stdoutContent.trim()

        if (versionContent.includes(version)) {
            step.info(`${command}@${version} is already installed.`);
            return true;
        } else {
            return false;
        }
    } catch (error) {
        step.info(`Error while checking ${command} version: ${error}`);
        return false;
    }
}
